<?php

namespace easy;

use Dotenv\Dotenv;
use support\Config;
use Workerman\Worker;
use Workerman\Protocols\Http;
use Workerman\Connection\TcpConnection;
use Webman\Config as EasyConfig;
use support\debug\SqlListen;
use Webman\App;
use support\Request;
use support\bootstrap\Log;
use support\bootstrap\Container;
use Webman\Route;
use Webman\Middleware;


class Start
{
    /**
     * @var array 配置
     */
    private $configData = [];

    /**
     * @var \string[][]  配置目录
     */
    private $configPath = [];

    /**
     * @var \string[][] 需要忽略加载的配置文件
     */
    private $excludeConfigFiles = [];

    /**
     * @var string 路由的文件
     */
    private $routePath = "";

    public function __construct()
    {
        $this->configPath = [
            '' => config_path()
        ];

        $this->excludeConfigFiles = [
            '' => ['route' , 'container']
        ];

        $this->configData = [
            'path' => $this->configPath,
            'exclude_files' => $this->excludeConfigFiles,
        ];

        $this->routePath = config_path() . '/route.php';
    }

    /**
     * 加载env
     */
    protected function loadEnv() : void
    {
        Dotenv::createMutable(base_path())->load();
    }

    /**
     * 加载配置
     */
    protected function loadConfig():void
    {
        Config::init($this->configPath , $this->excludeConfigFiles );
    }

    /**
     * 设置时区
     */
    protected function setTimezone() :void
    {
        $timezone = EasyConfig::get('app.default_timezone' , 'Asia/Shanghai');
        if($timezone){
            date_default_timezone_set($timezone);
        }
    }

    /**
     * 加载worker
     */
    protected function workerLoad():void
    {
        $config = EasyConfig::get('server');
        $configData = $this->configData;

        Worker::$onMasterReload = function (){
            if (function_exists('opcache_get_status')) {
                if ($status = opcache_get_status()) {
                    foreach (array_keys($status['scripts']) as $file) {
                        opcache_invalidate($file, true);
                    }
                }
            }
        };
        Worker::$pidFile                      = $config['pid_file'];
        Worker::$stdoutFile                   = $config['stdout_file'];
        TcpConnection::$defaultMaxPackageSize = $config['max_package_size'] ?? 10*1024*1024;

        $worker = new Worker($config['listen'], $config['context']);
        $property_map = [ 'name', 'count', 'user', 'group', 'reusePort', 'transport' ];
        foreach ($property_map as $property) {
            if (isset($config[$property])) {
                $worker->$property = $config[$property];
            }
        }

        $worker->onWorkerStart = function ($worker) use($configData) {
            Dotenv::createMutable(base_path())->load();
            Config::reInit($configData['path'] , $configData['exclude_files'] ?? []);
            foreach (config('bootstrap', []) as $class_name) {
                /** @var \Webman\Bootstrap $class_name */
                $class_name::start($worker);
            }

            //判断是否运行以下的debug
            $debug =  Config::get('app.debug' , true);
            if($debug === true){
                SqlListen::handle();
            }

            $app = new App($worker, Container::instance(), Log::channel('default') , app_path(), public_path());

            /**
             * 加载路由文件
             */
            Route::load($this->routePath);


            /**
             * 加载中间件文件
             */
            Middleware::load(EasyConfig::get('middleware', []));
            Middleware::load(['__static__' => config('static.middleware', [])]);

            Http::requestClass(Request::class);
            $worker->onMessage = [$app, 'onMessage'];
        };

    }

    /**
     * 加载其他进程
     */
    protected function processWorker() :void
    {
        $processConfig = EasyConfig::get('process', []);
        if(!empty($processConfig)){
            $configData = $this->configData;
            foreach($processConfig as $processName => $config){
                $worker = new Worker($config['listen'] ?? null, $config['context'] ?? []);
                $propertyMap = [ 'count', 'user', 'group', 'reloadable', 'reusePort', 'transport', 'protocol'];
                $worker->name = $processName;

                foreach ($propertyMap as $property) {
                    if (isset($config[$property])) {
                        $worker->$property = $config[$property];
                    }
                }
                $worker->onWorkerStart = function ($worker) use ($config , $configData) {
                    Dotenv::createMutable(base_path())->load();
                    Config::reInit($configData['path'] , $configData['exclude_files'] ?? []);

                    $bootstrap = $config['bootstrap'] ?? EasyConfig::get('bootstrap', []);
                    if (!in_array(Log::class, $bootstrap)) {
                        $bootstrap[] = Log::class;
                    }
                    foreach ($bootstrap as $className) {
                        /** @var \Webman\Bootstrap $className */
                        $className::start($worker);
                    }

                    if(isset($config['handler'])){
                        if (!class_exists($config['handler'])) {
                            echo "process error: class {$config['handler']} not exists\r\n";
                            return;
                        }
                        $class = Container::make($config['handler'], $config['constructor'] ?? []);
                        worker_bind($worker, $class);
                    }elseif( isset($config['services']) ){

                        foreach ($config['services'] ?? [] as $server) {
                            if (!class_exists($server['handler'])) {
                                echo "process error: class {$server['handler']} not exists\r\n";
                                continue;
                            }
                            $listen = new Worker($server['listen'] ?? null, $server['context'] ?? []);
                            if (isset($server['listen'])) {
                                echo "listen: {$server['listen']}\n";
                            }
                            $class = Container::make($server['handler'], $server['constructor'] ?? []);
                            worker_bind($listen, $class);
                            $listen->listen();
                        }

                    }

                };
            }
        }
    }

    /**
     * 开始
     */
    public function run()
    {
        $this->loadEnv();
        $this->loadConfig();
        $this->setTimezone();
        $this->workerLoad();
        $this->processWorker();
    }


}